package com.jee.ssm.common.socket.guideScreen;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;

import com.jee.ssm.common.utils.SpringContextHolder;
import com.jee.ssm.common.utils.UUIDFactory;
import com.jee.ssm.model.GuideScreen;
import com.jee.ssm.modules.guideScreen.services.GuideScreenService;

public class SocketHandle implements Runnable {
	
	private Logger logger = Logger.getLogger(SocketHandle.class);

    private ApplicationContext applicationContext = SpringContextHolder.getApplicationContext();

    private Socket socket;

    private boolean isConnected = false;
    private boolean firstConnected = false;
    
    private String number;
    private GuideScreen gs = null;
    
    private int heartBeat = 0;

    private InputStream is;
    private OutputStream os;
	
	public SocketHandle(Socket socket) {
        this.socket = socket;
        try {
            this.socket.setKeepAlive(true);
            this.socket.setSoTimeout(1000*60*60*2);
            this.is = socket.getInputStream();
            this.os = socket.getOutputStream();
            this.isConnected = true;
            this.firstConnected = true;
        } catch(IOException e) {
            e.printStackTrace();
        }
    }

	@Override
	public void run() {
		try {
			while(this.isConnected) {
				try {
					byte[] b = new byte[2048];
		            int len = this.is.read(b);
		            Thread.sleep(1);
		            if(len > 0) {
		            	int[] arrayInt = getArrayInt(b, len);
						handleArray(arrayInt);
		            }
				} catch(IOException e) {
					e.printStackTrace();
					logger.info(gs.getName() + "处理数据异常");
					removeSocket();
				} catch(Exception e) {
					e.printStackTrace();
					logger.info(gs.getName() + "处理未知异常");
					removeSocket();
				}
	        }
		} catch(Exception e) {
			e.printStackTrace();
			logger.info(gs.getName() + "while外异常");
			removeSocket();
		}
	}

	private void removeSocket() {
		this.isConnected = false;
		SocketServer.socketMap.remove(this.number);
		logger.info(gs.getName() + "从map里面去掉");
		if(this.is != null) {
			try {
				this.is.close();
			} catch (IOException e) {
				logger.info(gs.getName() + "InputStream关闭异常");
				e.printStackTrace();
			}
		}
		if(this.os != null) {
			try {
				this.os.close();
			} catch (IOException e) {
				logger.info(gs.getName() + "OutputStream关闭异常");
				e.printStackTrace();
			}
		}
		if(this.socket != null) {
			try {
				this.socket.close();
			} catch (IOException e) {
				logger.info(gs.getName() + "socket关闭异常");
				e.printStackTrace();
			}
		}

//		logger.info("是否绑定" + socket.isBound()); // 是否绑定
//		logger.info("是否关闭" + socket.isClosed()); // 是否关闭
//		logger.info("是否连接" + socket.isConnected()); // 是否连接
//		logger.info("是否关闭输入流" + socket.isInputShutdown()); // 是否关闭输入流
//		logger.info("是否关闭输出流" + socket.isOutputShutdown()); // 是否关闭输出流
	}
	
	private void handleArray(int[] arrayInt) {
//		String agreeContent = getAgreementContent(arrayInt);
		if(arrayInt[0] == 170 && arrayInt[arrayInt.length - 3] == 204) {
			
			String agreeCmd = "0x" + HandleUtils.get16Str(arrayInt[3]);
//			logger.info("[协议【" + agreeCmd + "】内容:" + agreeContent + "]");
			if("0x00".equals(agreeCmd)) {
				//回复心跳
				replyHeart();
		        String message = new String(arrayInt, 4, 9);
//		        logger.info(message);
		        
				if(firstConnected) {
					this.number = message;
					SocketServer.socketMap.put(message, this);
					List<GuideScreen> list = ((GuideScreenService)(this.applicationContext.getBean("guideScreenService"))).findByNumber(message);

					if(list.size() == 0) {
						gs = new GuideScreen(UUIDFactory.getStringId(), message, 0, 0, new Date(), new Date(),new Date());
						logger.info(gs.getName()+"发来心跳时间："+gs.getLastTime());
						try {
							((GuideScreenService)(this.applicationContext.getBean("guideScreenService"))).insert(gs);
						} catch (Exception e) {
							e.printStackTrace();
							logger.info("诱导屏注册失败");
						}
					} else {
						this.gs = list.get(0);
					}
					firstConnected = false;
				} else {
					if(SocketServer.socketMap.get(message) == null) {
			        	SocketServer.socketMap.put(message, this);
			        }
					if(gs != null) {
						heartBeat ++;
						if(heartBeat > 9) {
							heartBeat = 0;
							gs.setLastTime(new Date());
							try {
								((GuideScreenService)(this.applicationContext.getBean("guideScreenService"))).refreshLastTime(gs);
							} catch(Exception e) {
								e.printStackTrace();
								logger.info("更新心跳时间异常");
							}
						}
					}
				}
			}
			
			if("0x12".equals(agreeCmd)) {
				if(arrayInt[4] == 1) {
//					logger.info("文件名发送成功");
				} else if(arrayInt[4] == 0) {
					logger.info(this.gs.getName() + "文件名发送失败");
				} else if(arrayInt[4] == 2) {
					logger.info(this.gs.getName() + "文件已存在");
				} else {
					logger.info(this.gs.getName() + "文件名发送未知错误：" + arrayInt[4]);
				}
			}
			
			if("0x1C".equals(agreeCmd)) {
				GuideScreen guideScreen = new GuideScreen(this.gs.getId());
				if(arrayInt[4] == 1) {
					guideScreen.setLatestResult(UUIDFactory.getCurrentTime() + "  播放文件成功");
				} else if(arrayInt[4] == 0) {
					guideScreen.setLatestResult(UUIDFactory.getCurrentTime() + "  播放文件失败");
				} else {
					guideScreen.setLatestResult(UUIDFactory.getCurrentTime() + "  播放文件未知错误");
				}
				try {
					((GuideScreenService)(this.applicationContext.getBean("guideScreenService"))).editResult(guideScreen);
				} catch (Exception e) {
					e.printStackTrace();
					logger.info("保存播放结果失败");
				}
			}
			
			if("0x7D".equals(agreeCmd)) {
				if(arrayInt[4] == 1) {
//					logger.info("文件清理成功");
				} else if(arrayInt[4] == 0) {
					logger.info(this.gs.getName() + "文件清理失败");
				} else {
					logger.info(this.gs.getName() + "文件清理未知错误：" + arrayInt[4]);
				}
			}
			
			if("0x14".equals(agreeCmd)) {
//				logger.info("文件发送：" + arrayInt[4] + "  " + arrayInt[5] + "  " + arrayInt[6]);
				if(arrayInt[6] == 1) {
//					logger.info("文件发送成功");
				} else if(arrayInt[6] == 0) {
					logger.info(this.gs.getName() + "文件发送失败");
				} else {
					logger.info(this.gs.getName() + "文件发送未知错误：" + arrayInt[6]);
				}
			}
			
			if("0xF9".equals(agreeCmd)) {
//				logger.info("文件发送完毕：" + arrayInt[4]);
				if(arrayInt[4] == 1) {
//					logger.info("文件发送完毕返回成功");
				} else if(arrayInt[4] == 0) {
					logger.info(this.gs.getName() + "文件发送完毕返回失败");
				} else {
					logger.info(this.gs.getName() + "文件发送完毕返回未知错误：" + arrayInt[4]);
				}
			}
		} else {
			String agreeContent = getAgreementContent(arrayInt);
			String agreeCmd = "0x" + HandleUtils.get16Str(arrayInt[3]);
			logger.info("[错误协议【" + agreeCmd + "】内容:" + agreeContent + "]");
		}
	}
	
	/**
	 * 回复心跳
	 */
	private void replyHeart() {

		List<Byte> bytes = new ArrayList<>(HandleUtils.getHead());
        bytes.add((byte)0x00);
        int mills = ((Long)(System.currentTimeMillis() / 1000)).intValue();
        bytes.add((byte) (mills & 0xff));// 最低位   
        bytes.add((byte) ((mills >> 8) & 0xff));// 次低位   
        bytes.add((byte) ((mills >> 16) & 0xff));// 次高位   
        bytes.add((byte) (mills >>> 24));// 最高位,无符号右移。 
        bytes = HandleUtils.getFoot(bytes);
        Byte[] byteArr = new Byte[bytes.size()];
        byteArr = bytes.toArray(byteArr);
        
        try {
        	this.os.write(ArrayUtils.toPrimitive(byteArr));
            this.os.flush();
        } catch(IOException e) {
        	logger.info(this.gs.getName() + "回复心跳失败");
        	removeSocket();
        }
        
	}
	
	/**
	 * 1：亮屏，0：灭屏
	 * @param flag
	 */
	public void openOrClose(Integer flag) {
		List<Byte> bytes = new ArrayList<>(HandleUtils.getHead());
        bytes.add((byte)0x05);
        bytes.add(flag.byteValue());
        bytes = HandleUtils.getFoot(bytes);
        Byte[] byteArr = new Byte[bytes.size()];
        byteArr = bytes.toArray(byteArr);
        
        byte[] bs = ArrayUtils.toPrimitive(byteArr);
//        int[] a = getArrayInt(bs, bs.length);
//        logger.info("[返回内容:" + getAgreementContent(a) + "]");
        try {
        	this.os.write(bs);
            this.os.flush();
        } catch(IOException e) {
        	logger.info("发送失败");
        	removeSocket();
        }
	}
	
	/**
	 * 媒体文件清理
	 */
	public void cleanAll() {
		List<Byte> bytes = new ArrayList<>(HandleUtils.getHead());
        bytes.add((byte)0x7C);
        bytes.add((byte)0x00);

        bytes = HandleUtils.getFoot(bytes);
        Byte[] byteArr = new Byte[bytes.size()];
        byteArr = bytes.toArray(byteArr);
        
        byte[] bs = ArrayUtils.toPrimitive(byteArr);
//        int[] a = getArrayInt(bs, bs.length);
//        logger.info("[媒体文件清理:" + getAgreementContent(a) + "]");
        try {
        	this.os.write(bs);
            this.os.flush();
        } catch(IOException e) {
        	logger.info(this.gs.getName() + "媒体文件清理失败");
        	removeSocket();
        }
	}
	
	/**
	 * 发送文件名称
	 * @param length 文件长度
	 * @param name 文件名称
	 */
	public void sendFileName(Integer length, String name) {
		List<Byte> bytes = new ArrayList<>(HandleUtils.getHead());
        bytes.add((byte)0x11);
        byte[] file = name.getBytes();
        bytes.add((byte) (length % 256));
        bytes.add((byte) (length / 256));

		bytes.add((byte)0xFF);
		bytes.add((byte)0xFF);

        for(byte y : file) {
        	bytes.add(y);
        }
        bytes = HandleUtils.getFoot(bytes);
        Byte[] byteArr = new Byte[bytes.size()];
        byteArr = bytes.toArray(byteArr);
        
        byte[] bs = ArrayUtils.toPrimitive(byteArr);
//        int[] a = getArrayInt(bs, bs.length);
//        logger.info("[返回内容:" + getAgreementContent(a) + "]");
        try {
        	this.os.write(bs);
            this.os.flush();
        } catch(IOException e) {
        	logger.info(this.gs.getName() + "发送文件名失败");
        	removeSocket();
        }
	}
	
	/**
	 * 发送文件
	 * @param list 文件的byte数组
	 */
	public void sendFile(List<Byte> list) {
		List<Byte> bytes = new ArrayList<>(HandleUtils.getHead());
        bytes.add((byte)0x13);
        bytes.add((byte) (1 % 256));
        bytes.add((byte) (1 / 256));
        bytes.addAll(list);

        bytes = HandleUtils.getFoot(bytes);
        Byte[] byteArr = new Byte[bytes.size()];
        byteArr = bytes.toArray(byteArr);
        
        byte[] bs = ArrayUtils.toPrimitive(byteArr);
//        int[] a = getArrayInt(bs, bs.length);
//        logger.info("[发送文件内容:" + getAgreementContent(a) + "]");
        try {
			DataOutputStream dos = new DataOutputStream(this.os);
        	dos.write(bs);
            dos.flush();
        } catch(IOException e) {
        	logger.info(this.gs.getName() + "发送文件失败");
        	removeSocket();
        }
	}
	
	/**
	 * 播放
	 * @param n 播放列表序号
	 */
	public void play(Integer n) {
		List<Byte> bytes = new ArrayList<>(HandleUtils.getHead());
        bytes.add((byte)0x1B);
        bytes.add(n.byteValue());

        bytes = HandleUtils.getFoot(bytes);
        Byte[] byteArr = new Byte[bytes.size()];
        byteArr = bytes.toArray(byteArr);
        
        byte[] bs = ArrayUtils.toPrimitive(byteArr);
//        int[] a = getArrayInt(bs, bs.length);
//        logger.info("[播放文件:" + getAgreementContent(a) + "]");
        try {
        	this.os.write(bs);
            this.os.flush();
        } catch(IOException e) {
        	logger.info(this.gs.getName() + "播放文件发送失败");
        	removeSocket();
        }
	}
	
	/**
	 * byte数组转十进制数组
	 * @param bytes
	 * @param len
	 * @return
	 */
	private int[] getArrayInt(byte[] bytes, int len) {
        int[] arrayInt = new int[len];
        for(int l = 0;l < len; l ++) {
            byte b = bytes[l];
            arrayInt[l] = b < 0 ? b + 256 : b;
        }
        return arrayInt;
    }
	
	/**
     * 协议内容
    * @param arrayInt 内容数组
    * @return 协议内容
    */
   private String getAgreementContent(int[] arrayInt) {
       StringBuilder str = new StringBuilder();
       str.append("[");
       for(int anArrayInt : arrayInt) {
           str.append(anArrayInt).append(" ");
       }
       str.append("]");
       return str.toString();
   }

}
